home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Fatted Calf
/
The Fatted Calf.iso
/
Applications
/
Compression
/
Opener
/
Source
/
Process.m
< prev
next >
Wrap
Text File
|
1993-07-19
|
7KB
|
273 lines
/*
* Process -- manage i/o with simple subprocesses.
* Considerably tweaked relative of /NextDeveloper/Examples/Subprocess.
* M. J. Hawley
* Copyright (c) MIT Media Laboratory
* mike@media-lab.mit.edu
*/
#import "Process.h"
@interface Process(Private)
- childDidExit;
- fdHandler:(int)theFd;
@end
static void
showError (const char *s, id delegate){ // ensure errors never get lost
if (delegate && [delegate respondsTo:@selector(processError:)])
[delegate perform:@selector(processError:) with:(void *)s];
else if (NXApp) // no delegate, but we're running w/in an App
NXRunAlertPanel(0, s, 0, 0, 0);
else
perror(s);
}
static void
fdHandler (int fd, id self) { // DPS handler for output from process
[self fdHandler:fd];
}
static void
getptys(int *master, int *slave){ // attempt to setup the ptys
#define PTY_TEMPLATE "/dev/pty??"
#define PTY_LENGTH 11
char device[PTY_LENGTH];
char *block, *num;
char *blockLoc; // specifies the location of block for the device string
char *numLoc; // specifies the pty name with the digit ptyxD
char *msLoc; // specifies the master (ptyxx) or slave (ttyxx)
struct sgttyb setp = {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)};
struct tchars setc = {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK};
struct ltchars sltc = {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT};
int lset = (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ);
int setd = NTTYDISC;
strcpy(device, PTY_TEMPLATE); // string constants are not writable
blockLoc = &device[ strlen("/dev/pty") ];
numLoc = &device[ strlen("/dev/pty?") ];
msLoc = &device[ strlen("/dev/") ];
for (block = "pqrs"; *block; block++){
*blockLoc = *block;
for (num = "0123456789abcdef"; *num; num++) {
*numLoc = *num;
*master = open(device, O_RDWR);
if (*master >= 0) {
*msLoc = 't';
*slave = open(device, O_RDWR);
if (*slave >= 0) {
(void) ioctl(*slave, TIOCSETP, (char *)&setp);
(void) ioctl(*slave, TIOCSETC, (char *)&setc);
(void) ioctl(*slave, TIOCSETD, (char *)&setd);
(void) ioctl(*slave, TIOCSLTC, (char *)&sltc);
(void) ioctl(*slave, TIOCLSET, (char *)&lset);
return;
}
}
} /* hunting through a bank of ptys */
} /* hunting through blocks of ptys in all the right places */
*master = -1;
*slave = -1;
}
static int
iwait(fd, timeout)
long fd;
unsigned long timeout; /* in seconds */
/*
* Wait until 'fd' is ready for reading, or 'timeout'.
* Return '>=0' when 'fd' is readable, '0' if timeout, '-1' on error.
* Example: 'iwait(f,0)' polls a file descriptor
* without blocking and returns true if it's readable;
* e.g., 'iwait(0,0)' is true when standard input is contains something.
*/
{
struct timeval t;
int readfd = 1<<fd;
t.tv_sec = timeout, t.tv_usec = 0;
return (int)select(sizeof(int)*8, (fd_set *)&readfd, (fd_set *)0, (fd_set *)0, &t);
}
@implementation Process(Private)
- childDidExit { // cleanup after a child process exits
if (childPid) {
if (from) DPSRemoveFD(from);
close(from);
if (fpTo) fclose(fpTo);
if (fpFrom) fclose(fpFrom);
fpTo = fpFrom = (FILE *)0;
childPid=0; // specify that child is dead
if (delegate && [delegate respondsTo:@selector(processDone)])
[delegate perform:@selector(processDone)];
}
return self;
}
- fdHandler:(int)fd { // DPS handler for output from process
if (((bufferCount=read(fd,buffer,BUFSIZE-1))<0)||(!bufferCount))
return [self childDidExit];
buffer[bufferCount] = '\0';
if (delegate && [delegate respondsTo:action])
[delegate perform:action with:(void *)&buffer];
return self;
}
@end
@implementation Process
+ new:(char *)process delegate:del {
self = [Process alloc];
[self init:process delegate:del andPty:YES andStderr:YES];
return self;
}
+ new:(char *)process delegate:del andPty:(BOOL)wantsPty andStderr:(BOOL)wantsStderr {
self = [Process alloc];
[self init:process delegate:del andPty:wantsPty andStderr:wantsStderr];
return self;
}
- init:(char *)process delegate:del {
return [self init:process delegate:del andPty:NO andStderr:NO];
}
- init:(char *)process delegate:del andPty:(BOOL)pty andStderr:(BOOL)err {
// initializes an instance of process and corresponding UNIX process
int pipeTo[2];
int pipeFrom[2];
int tty, numFds, fd; // for temporary use
int processGroup;
int pidChild; // needed because childPid does not exist
// until process is instantiated
[self setAction:@selector(processOutput:)];
if (pty){
tty = open("/dev/tty", O_RDWR);
getptys(&masterPty,&slavePty);
if (masterPty <= 0 || slavePty <= 0) {
showError("Error grabbing ptys for subprocess.", del);
return self;
}
// remove the controlling tty if launched from a shell,
// but not Workspace;
// so that we have job control over the parent application in shell
// and so that subprocesses can be restarted in Workspace
if ((tty<0) && ((tty = open("/dev/tty", 2))>=0)) {
ioctl(tty, TIOCNOTTY, 0);
close(tty);
}
} else
if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0){
showError("Error starting UNIX pipes to process.", del);
return self;
}
switch (pidChild = vfork()){
case -1: // error
showError("Error starting UNIX vfork of process.", del);
return self;
case 0: // child
if (pty) {
dup2(slavePty, 0);
dup2(slavePty, 1);
if (err) dup2(slavePty, 2);
} else {
dup2(pipeTo[0], 0);
dup2(pipeFrom[1], 1);
if (err) dup2(pipeFrom[1], 2);
}
numFds = getdtablesize();
for (fd=3; fd<numFds; fd++)
close(fd);
processGroup = getpid();
ioctl(0, TIOCSPGRP, (char *)&processGroup);
setpgrp (0, processGroup);
// execl(process, 0);
execl("/bin/sh", "sh", "-c", process, 0);
perror("vfork (child)"); // should never gets here tho
exit(1);
default: // parent
[self setDelegate:del];
childPid = pidChild;
if (pty){
close(slavePty);
fpTo = fdopen(masterPty, "w");
from = masterPty;
fpFrom = fdopen(masterPty, "r");
} else {
close(pipeTo[0]);
close(pipeFrom[1]);
fpTo = fdopen(pipeTo[1], "w");
from = pipeFrom[0];
fpFrom = fdopen(pipeFrom[0], "r");
}
setbuf(fpTo, NULL);
setbuf(fpFrom, NULL);
DPSAddFD(from,(DPSFDProc)fdHandler,(id)self,NX_MODALRESPTHRESHOLD+1);
// printf("added %d, %d, %d [%d %d]\n",from, self, delegate, pipeTo[1], pipeFrom[0]);
return self;
}
}
- puts:(char *)s {
fputs(s, fpTo);
return self;
}
static void
_fgets(s,n,f) char *s; int n; FILE *f; {
int fd = fileno(f);
while (iwait(fd,3)>0){
if (read(fd,s,1) != 1){
*s = '\0';
return;
}
if (*s=='\n' || --n <= 0){
*++s = '\0';
return ;
}
if (*s != '\r') ++s;
}
*++s = '\0';
}
- gets:(char *)s :(int)n {
*s = '\0';
_fgets(s,n,fpFrom);
return self;
}
- terminate:sender{
if (childPid){
kill(childPid+1, SIGTERM);
[self childDidExit];
}
return self;
}
- setDelegate:anObject {
delegate = anObject;
return self;
}
- delegate {
return delegate;
}
- setAction:(SEL)theAction {
action = theAction;
return self;
}
@end